<!DOCTYPE HTML>
<html>
<head>
  <meta http-equiv="X-UA-Compatible" content="IE=9; IE=8; IE=7; IE=EDGE"/>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
  <title>Teamwork</title>

  <link rel=stylesheet href="platform.css" type="text/css">
  <link rel=stylesheet href="libs/jquery/dateField/jquery.dateField.css" type="text/css">

  <link rel=stylesheet href="gantt.css" type="text/css">
  <link rel=stylesheet href="ganttPrint.css" type="text/css" media="print">
  <link rel=stylesheet href="libs/jquery/valueSlider/mb.slider.css" type="text/css" media="print">

  <script src="libs/jquery/jquery.min.js"></script>
  <script src="libs/jquery/jquery-ui.min.js"></script>

  <script src="libs/jquery/jquery.livequery.1.1.1.min.js"></script>
  <script src="libs/jquery/jquery.timers.js"></script>

  <script src="libs/utilities.js"></script>
  <script src="libs/forms.js"></script>
  <script src="libs/date.js"></script>
  <script src="libs/dialogs.js"></script>
  <script src="libs/layout.js"></script>
  <script src="libs/i18nJs.js"></script>
  <script src="libs/jquery/dateField/jquery.dateField.js"></script>
  <script src="libs/jquery/JST/jquery.JST.js"></script>
  <script src="libs/jquery/valueSlider/jquery.mb.slider.js"></script>

  <script type="text/javascript" src="libs/jquery/svg/jquery.svg.min.js"></script>
  <script type="text/javascript" src="libs/jquery/svg/jquery.svgdom.1.8.js"></script>


  <script src="ganttUtilities.js"></script>
  <script src="ganttTask.js"></script>
  <script src="ganttDrawerSVG.js"></script>
  <script src="ganttZoom.js"></script>
  <script src="ganttGridEditor.js"></script>
  <script src="ganttMaster.js"></script>


  <!--<script src="libs/profiling.js"></script>-->
  <!--<script type="text/javascript" src="ganttTestSuite.js"></script>-->
</head>
<body style="background-color: #fff;">


<div id="workSpace" style="padding:0px; overflow-y:auto; overflow-x:hidden;border:1px solid #e5e5e5;position:relative;margin:0 5px"></div>

<style>
  .resEdit {
    padding: 15px;
  }

  .resLine {
    width: 95%;
    padding: 3px;
    margin: 5px;
    border: 1px solid #d0d0d0;
  }

  body {
    overflow: hidden;
  }

  .ganttButtonBar h1{
    color: #000000;
    font-weight: bold;
    font-size: 28px;
    margin-left: 10px;
  }

</style>

<form id="gimmeBack" style="display:none;" action="../gimmeBack.jsp" method="post" target="_blank"><input type="hidden" name="prj" id="gimBaPrj"></form>

<script type="text/javascript">

var ge; // GanttMaster instance
// QWebChannel bridge object, will be initialized after page load
// var ganttBridge; // Declared in the script injected by Python

$(function() {
  var canWrite=true; // Assuming write permissions initially

  // Initialize GanttMaster
  ge = new GanttMaster();
  ge.set100OnClose=true;
  ge.shrinkParent=true;
  ge.init($("#workSpace"));
  loadI18n(); // Load localization

  // Best-fitting zoom level will be computed automatically if needed
  delete ge.gantt.zoom;

  // Initial data loading is now triggered by Python via QWebChannel
  // after the page and QWebChannel are fully loaded.
  // We don't call ge.loadProject here anymore.
  console.log("Gantt initialized. Waiting for QWebChannel connection and data load from Python...");

  // Example: Add a save button if it doesn't exist in the templates
  // This depends on how your buttons are structured. Assuming a div with class 'ganttButtonBar'
  if ($(".ganttButtonBar").find("#saveButton").length === 0) {
      var saveButton = $("<button id='saveButton' class='button textual icon requireWrite' title='保存'><span class='teamworkIcon'>&#x4f;</span></button>");
      // Append it to a suitable location, e.g., next to other buttons
      // $(".ganttButtonBar .buttons").append(saveButton); // Adjust selector as needed
      // Or find an existing save button and re-bind its click event
  }

  // Bind the save function to the save button's click event
  // Ensure this runs *after* QWebChannel is likely initialized, or use event delegation
  $(document).on('click', '#saveButton, #saveGanttButton', function() { // Bind to existing or new button
      saveGanttData();
  });


  // Comment out or remove original history management if not used or needs rework with QWebChannel
  // initializeHistoryManagement(ge.tasks[0].id);
});




function getDemoProject(){
  //console.debug("getDemoProject")
ret= {"tasks":    [
      {"id": -1, "name": "Gantt editor", "progress": 0, "progressByWorklog": false, "relevance": 0, "type": "", "typeId": "", "description": "", "code": "", "level": 0, "status": "STATUS_ACTIVE", "depends": "", "canWrite": true, "start": 1396994400000, "duration": 20, "end": 1399586399999, "startIsMilestone": false, "endIsMilestone": false, "collapsed": false, "assigs": [], "hasChild": true},
      {"id": -2, "name": "coding", "progress": 0, "progressByWorklog": false, "relevance": 0, "type": "", "typeId": "", "description": "", "code": "", "level": 1, "status": "STATUS_ACTIVE", "depends": "", "canWrite": true, "start": 1396994400000, "duration": 10, "end": 1398203999999, "startIsMilestone": false, "endIsMilestone": false, "collapsed": false, "assigs": [], "hasChild": true},
      {"id": -3, "name": "gantt part", "progress": 0, "progressByWorklog": false, "relevance": 0, "type": "", "typeId": "", "description": "", "code": "", "level": 2, "status": "STATUS_ACTIVE", "depends": "", "canWrite": true, "start": 1396994400000, "duration": 2, "end": 1397167199999, "startIsMilestone": false, "endIsMilestone": false, "collapsed": false, "assigs": [], "hasChild": false},
      {"id": -4, "name": "editor part", "progress": 0, "progressByWorklog": false, "relevance": 0, "type": "", "typeId": "", "description": "", "code": "", "level": 2, "status": "STATUS_SUSPENDED", "depends": "3", "canWrite": true, "start": 1397167200000, "duration": 4, "end": 1397685599999, "startIsMilestone": false, "endIsMilestone": false, "collapsed": false, "assigs": [], "hasChild": false},
      {"id": -5, "name": "testing", "progress": 0, "progressByWorklog": false, "relevance": 0, "type": "", "typeId": "", "description": "", "code": "", "level": 1, "status": "STATUS_SUSPENDED", "depends": "2:5", "canWrite": true, "start": 1398981600000, "duration": 5, "end": 1399586399999, "startIsMilestone": false, "endIsMilestone": false, "collapsed": false, "assigs": [], "hasChild": true},
      {"id": -6, "name": "test on safari", "progress": 0, "progressByWorklog": false, "relevance": 0, "type": "", "typeId": "", "description": "", "code": "", "level": 2, "status": "STATUS_SUSPENDED", "depends": "", "canWrite": true, "start": 1398981600000, "duration": 2, "end": 1399327199999, "startIsMilestone": false, "endIsMilestone": false, "collapsed": false, "assigs": [], "hasChild": false},
      {"id": -7, "name": "test on ie", "progress": 0, "progressByWorklog": false, "relevance": 0, "type": "", "typeId": "", "description": "", "code": "", "level": 2, "status": "STATUS_SUSPENDED", "depends": "6", "canWrite": true, "start": 1399327200000, "duration": 3, "end": 1399586399999, "startIsMilestone": false, "endIsMilestone": false, "collapsed": false, "assigs": [], "hasChild": false},
      {"id": -8, "name": "test on chrome", "progress": 0, "progressByWorklog": false, "relevance": 0, "type": "", "typeId": "", "description": "", "code": "", "level": 2, "status": "STATUS_SUSPENDED", "depends": "6", "canWrite": true, "start": 1399327200000, "duration": 2, "end": 1399499999999, "startIsMilestone": false, "endIsMilestone": false, "collapsed": false, "assigs": [], "hasChild": false}
    ], "selectedRow": 2, "deletedTaskIds": [],
      "resources": [
      {"id": "tmp_1", "name": "Resource 1"},
      {"id": "tmp_2", "name": "Resource 2"},
      {"id": "tmp_3", "name": "Resource 3"},
      {"id": "tmp_4", "name": "Resource 4"}
    ],
      "roles":       [
      {"id": "tmp_1", "name": "Project Manager"},
      {"id": "tmp_2", "name": "Worker"},
      {"id": "tmp_3", "name": "Stakeholder"},
      {"id": "tmp_4", "name": "Customer"}
    ], "canWrite":    true, "canDelete":true, "canWriteOnParent": true, canAdd:true}


    //actualize data
    var offset=new Date().getTime()-ret.tasks[0].start;
    for (var i=0;i<ret.tasks.length;i++) {
      ret.tasks[i].start = ret.tasks[i].start + offset;
    }
  return ret;
}



// This function is no longer needed as loading is handled by Python via QWebChannel
/*
function loadGanttFromServer(taskId, callback) {
  // ... (old code) ...
}
*/

function upload(uploadedFile) {
  var fileread = new FileReader();

  fileread.onload = function(e) {
    var content = e.target.result;
    var intern = JSON.parse(content); // Array of Objects.
    //console.log(intern); // You can index every object

    ge.loadProject(intern);
    ge.checkpoint(); //empty the undo stack

  };

  fileread.readAsText(uploadedFile);
}

// Renamed function to avoid conflict if original button still exists
function saveGanttData() {
  console.log("Attempting to save Gantt data via QWebChannel...");
  if (typeof ge === 'undefined') {
      console.error("GanttMaster 'ge' is not initialized.");
      alert("错误：甘特图未初始化！");
      return;
  }

  if (typeof window.ganttBridge === 'undefined' || typeof window.ganttBridge.save_gantt_data === 'undefined') {
      console.error("QWebChannel object 'ganttBridge' or 'save_gantt_data' function is not available.");
      // Attempt to re-initialize? Or just inform the user.
      alert("错误：无法连接到后端保存服务！请确保QWebChannel已正确加载。");
      // Try to initialize QWebChannel again (might indicate a loading order issue)
      // This is a fallback, ideally it should be ready.
      if (typeof QWebChannel !== 'undefined' && typeof qt !== 'undefined' && typeof qt.webChannelTransport !== 'undefined') {
          new QWebChannel(qt.webChannelTransport, function (channel) {
              window.ganttBridge = channel.objects.ganttBridge;
              console.log("Re-initialized QWebChannel. Please try saving again.");
              alert("已尝试重新连接后端，请再次尝试保存。");
          });
      }
      return;
  }

  var prj;
  try {
      prj = ge.saveProject();
      if (!prj || !prj.tasks) {
          console.error("ge.saveProject() returned invalid data:", prj);
          alert("错误：无法获取甘特图数据进行保存！");
          return;
      }
  } catch (e) {
      console.error("Error calling ge.saveProject():", e);
      alert("错误：调用 ge.saveProject() 时出错！");
      return;
  }

  var prjString;
  try {
      prjString = JSON.stringify(prj); // Stringify the project data
  } catch (e) {
      console.error("Error stringifying project data:", e, prj);
      alert("错误：无法序列化甘特图数据！");
      return;
  }


  console.log("Sending data to Python:", prjString);

  // Call the Python backend function via QWebChannel
  // The callback now receives a JSON string response
  window.ganttBridge.save_gantt_data(prjString, function(responseJson) {
      console.log("Save operation returned JSON:", responseJson);
      var response;
      try {
          response = JSON.parse(responseJson);
      } catch (e) {
          console.error("Error parsing JSON response from Python:", e, responseJson);
          alert("错误：无法解析来自后端的保存响应！");
          return;
      }

      if (response && response.success) {
          console.log("Data saved successfully (according to Python).");
          // --- Update IDs in the frontend Gantt object ---
          if (response.id_map && Object.keys(response.id_map).length > 0) {
              console.log("Updating frontend IDs:", response.id_map);
              for (var tempId in response.id_map) {
                  var persistentId = response.id_map[tempId];
                  var task = ge.getTask(tempId); // Find task by temporary ID
                  if (task) {
                      // Update the task's ID within the GanttMaster's internal structure
                      task.id = persistentId;
                      // Update the task map in GanttMaster
                      delete ge.tasks.map[tempId];
                      ge.tasks.map[persistentId] = task;
                      console.log(`Updated task ID in ge: ${tempId} -> ${persistentId}`);

                      // Also update the DOM element's taskId attribute if necessary
                      // This helps if other parts of the JS rely on the DOM attribute
                      var taskRow = ge.element.find("#tid_" + tempId);
                      if (taskRow.length > 0) {
                          taskRow.attr("id", "tid_" + persistentId);
                          taskRow.attr("taskId", persistentId);
                          console.log(`Updated DOM element ID for task: ${tempId} -> ${persistentId}`);
                      }
                       var taskBar = ge.element.find(".taskBox[taskId='" + tempId + "']");
                       if (taskBar.length > 0) {
                           taskBar.attr("taskId", persistentId);
                           console.log(`Updated task bar taskId attribute for task: ${tempId} -> ${persistentId}`);
                       }

                  } else {
                      console.warn(`Task with tempId ${tempId} not found in ge object after save.`);
                  }
              }
              // After updating IDs, it might be necessary to update dependencies
              // that referred to the old temporary IDs.

              // --- Update dependencies referencing old IDs ---
              console.log("Updating dependencies...");
              for (var i = 0; i < ge.tasks.length; i++) {
                  var taskToUpdate = ge.tasks[i];
                  // Check if the task object and its depends property exist
                  if (taskToUpdate && taskToUpdate.depends && typeof taskToUpdate.depends === 'string' && taskToUpdate.depends.length > 0) {
                      var originalDepends = taskToUpdate.depends;
                      var newDepends = originalDepends;
                      // Iterate through the map of changed IDs
                      for (var tempIdDep in response.id_map) {
                          // Ensure the key belongs to the id_map itself
                          if (response.id_map.hasOwnProperty(tempIdDep)) {
                              var persistentIdDep = response.id_map[tempIdDep];
                              // Use regex to replace tempId only when it appears as a whole ID (word boundary)
                              // Handles cases like "tmp_1", "tmp_1:5", but not "tmp_10" if tempIdDep is "tmp_1"
                              var regex = new RegExp("\\b" + tempIdDep.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&') + "\\b", "g"); // Escape regex special chars in tempId
                              newDepends = newDepends.replace(regex, persistentIdDep);
                          }
                      }
                      // If dependencies were changed, update the task object and the DOM input field
                      if (newDepends !== originalDepends) {
                          taskToUpdate.depends = newDepends;
                          console.log(`Updated dependencies for task ${taskToUpdate.id} (${taskToUpdate.name}): ${originalDepends} -> ${newDepends}`);
                          // Also update the input field in the grid editor if it's visible
                          var taskRowDep = ge.element.find("#tid_" + taskToUpdate.id); // Use updated ID
                          if (taskRowDep.length > 0) {
                              var dependsInput = taskRowDep.find("input[name='depends']");
                              if (dependsInput.length > 0) {
                                  dependsInput.val(newDepends);
                                  console.log(`Updated DOM input field for dependencies of task ${taskToUpdate.id}`);
                              }
                          }
                      }
                  }
              }
              console.log("Dependency update complete.");
              // --- End of new dependency update logic ---

              ge.redraw(); // Redraw to reflect potential changes
            }
          // ---------------------------------------------
          ge.checkpoint(); // Clear undo stack after successful save and ID update
      } else {
          // Error message is shown by Python's InfoBar
          console.error("Data saving failed (according to Python):", response ? response.error : "Unknown error");
          // alert("保存失败，请查看日志或联系管理员。"); // Redundant if InfoBar works
      }
  });
}

// Function to download data to a file
function download(data, filename, type) {
  var file = new Blob([data], {type: type});
  if (window.navigator.msSaveOrOpenBlob) // IE10+
    window.navigator.msSaveOrOpenBlob(file, filename);
  else { // Others
    var a = document.createElement("a"),
      url = URL.createObjectURL(file);
    a.href = url;
    a.download = filename;
    document.body.appendChild(a);
    a.click();
    setTimeout(function() {
      document.body.removeChild(a);
      window.URL.revokeObjectURL(url);
    }, 0);
  }
}

function newProject(){
  clearGantt();
}


function clearGantt() {
  ge.reset();
}

//-------------------------------------------  Get project file as JSON (used for migrate project from gantt to Teamwork) ------------------------------------------------------
function getFile() {
  $("#gimBaPrj").val(JSON.stringify(ge.saveProject()));
  $("#gimmeBack").submit();
  $("#gimBaPrj").val("");

  /*  var uriContent = "data:text/html;charset=utf-8," + encodeURIComponent(JSON.stringify(prj));
   neww=window.open(uriContent,"dl");*/
}


// These functions are no longer used for primary load/save
/*
function loadFromLocalStorage() {
  // ... (old code) ...
}

function saveInLocalStorage() {
  // ... (old code) ...
}
*/

// Ensure getDemoProject is removed or not called during initialization
/*
function getDemoProject(){
 // ... (old code) ...
}
*/

// Compatibility function: Redirect calls from old save function name to the new one
function saveGanttOnServer() {
  console.warn("Deprecated saveGanttOnServer() called. Redirecting to saveGanttData().");
  saveGanttData();
}


//-------------------------------------------  Open a black popup for managing resources. This is only an axample of implementation (usually resources come from server) ------------------------------------------------------
function editResources(){

  //make resource editor
  var resourceEditor = $.JST.createFromTemplate({}, "RESOURCE_EDITOR");
  var resTbl=resourceEditor.find("#resourcesTable");

  for (var i=0;i<ge.resources.length;i++){
    var res=ge.resources[i];
    resTbl.append($.JST.createFromTemplate(res, "RESOURCE_ROW"))
  }


  //bind add resource
  resourceEditor.find("#addResource").click(function(){
    resTbl.append($.JST.createFromTemplate({id:"new",name:"resource"}, "RESOURCE_ROW"))
  });

  //bind save event
  resourceEditor.find("#resSaveButton").click(function(){
    var newRes=[];
    //find for deleted res
    for (var i=0;i<ge.resources.length;i++){
      var res=ge.resources[i];
      var row = resourceEditor.find("[resId="+res.id+"]");
      if (row.length>0){
        //if still there save it
        var name = row.find("input[name]").val();
        if (name && name!="")
          res.name=name;
        newRes.push(res);
      } else {
        //remove assignments
        for (var j=0;j<ge.tasks.length;j++){
          var task=ge.tasks[j];
          var newAss=[];
          for (var k=0;k<task.assigs.length;k++){
            var ass=task.assigs[k];
            if (ass.resourceId!=res.id)
              newAss.push(ass);
          }
          task.assigs=newAss;
        }
      }
    }

    //loop on new rows
    var cnt=0
    resourceEditor.find("[resId=new]").each(function(){
      cnt++;
      var row = $(this);
      var name = row.find("input[name]").val();
      if (name && name!="")
        newRes.push (new Resource("tmp_"+new Date().getTime()+"_"+cnt,name));
    });

    ge.resources=newRes;

    closeBlackPopup();
    ge.redraw();
  });


  var ndo = createModalPopup(400, 500).append(resourceEditor);
}

function initializeHistoryManagement(taskId){

  //retrieve from server the list of history points in millisecond that represent the instant when the data has been recorded
  //response: {ok:true, historyPoints: [1498168800000, 1498600800000, 1498687200000, 1501538400000, …]}
  $.getJSON(contextPath+"/applications/teamwork/task/taskAjaxController.jsp", {CM: "GETGANTTHISTPOINTS", OBJID:taskId}, function (response) {

    //if there are history points
    if (response.ok == true && response.historyPoints && response.historyPoints.length>0) {

      //add show slider button on button bar
      var histBtn = $("<button>").addClass("button textual icon lreq30 lreqLabel").attr("title", "SHOW_HISTORY").append("<span class=\"teamworkIcon\">&#x60;</span>");

      //clicking it
      histBtn .click(function () {
        var el = $(this);
        var ganttButtons = $(".ganttButtonBar .buttons");

        //is it already on?
        if (!ge.element.is(".historyOn")) {
          ge.element.addClass("historyOn");
          ganttButtons.find(".requireCanWrite").hide();

          //load the history points from server again
          showSavingMessage();
          $.getJSON(contextPath + "/applications/teamwork/task/taskAjaxController.jsp", {CM: "GETGANTTHISTPOINTS", OBJID: ge.tasks[0].id}, function (response) {
            jsonResponseHandling(response);
            hideSavingMessage();
            if (response.ok == true) {
              var dh = response.historyPoints;
              if (dh && dh.length > 0) {
                //si crea il div per lo slider
                var sliderDiv = $("<div>").prop("id", "slider").addClass("lreq30 lreqHide").css({"display":"inline-block","width":"500px"});
                ganttButtons.append(sliderDiv);

                var minVal = 0;
                var maxVal = dh.length-1 ;

                $("#slider").show().mbSlider({
                  rangeColor : '#2f97c6',
                  minVal     : minVal,
                  maxVal     : maxVal,
                  startAt    : maxVal,
                  showVal    : false,
                  grid       :1,
                  formatValue: function (val) {
                    return new Date(dh[val]).format();
                  },
                  onSlideLoad: function (obj) {
                    this.onStop(obj);

                  },
                  onStart    : function (obj) {},
                  onStop     : function (obj) {
                    var val = $(obj).mbgetVal();
                    showSavingMessage();
                    /**
                     * load the data history for that milliseconf from server
                     * response in this format {ok: true, baselines: {...}}
                     *
                     * baselines: {61707: {duration:1,endDate:1550271599998,id:61707,progress:40,startDate:1550185200000,status:"STATUS_WAITING",taskId:"3055"},
                     *            {taskId:{duration:in days,endDate:in millis,id:history record id,progress:in percent,startDate:in millis,status:task status,taskId:"3055"}....}}                     */

                    $.getJSON(contextPath + "/applications/teamwork/task/taskAjaxController.jsp", {CM: "GETGANTTHISTORYAT", OBJID: ge.tasks[0].id, millis:dh[val]}, function (response) {
                      jsonResponseHandling(response);
                      hideSavingMessage();
                      if (response.ok ) {
                        ge.baselines=response.baselines;
                        ge.showBaselines=true;
                        ge.baselineMillis=dh[val];
                        ge.redraw();
                      }
                    })

                  },
                  onSlide    : function (obj) {
                    clearTimeout(obj.renderHistory);
                    var self = this;
                    obj.renderHistory = setTimeout(function(){
                      self.onStop(obj);
                    }, 200)

                  }
                });
              }
            }
          });


          // closing the history
        } else {
          //remove the slider
          $("#slider").remove();
          ge.element.removeClass("historyOn");
          if (ge.permissions.canWrite)
            ganttButtons.find(".requireCanWrite").show();

          ge.showBaselines=false;
          ge.baselineMillis=undefined;
          ge.redraw();
        }

      });
      $("#saveGanttButton").before(histBtn);
    }
  })
}

function showBaselineInfo (event,element){
  //alert(element.attr("data-label"));
  $(element).showBalloon(event, $(element).attr("data-label"));
  ge.splitter.secondBox.one("scroll",function(){
    $(element).hideBalloon();
  })
}

</script>




<div id="gantEditorTemplates" style="display:none;">
<div class="__template__" type="GANTBUTTONS">
  <!--
  <div class="ganttButtonBar noprint">
    <div class="buttons">
      <button onclick="ge.addNewRootTask();" class="button first big requireWrite" title="新建一级任务">新建任务</button>
    
      <button onclick="saveGanttData();" class="button first big requireWrite" title="保存">保存任务</button>
   
      <button onclick='ge.exportGantt();' class='button first big requireWrite'title="导出">导出任务</button>
    
      &nbsp; 
      <span class="ganttButtonSeparator"></span>
      <button onclick="$('#workSpace').trigger('undo.gantt');return false;" class="button textual icon requireCanWrite" title="撤销"><span class="teamworkIcon">'</span></button>
      <button onclick="$('#workSpace').trigger('redo.gantt');return false;" class="button textual icon requireCanWrite" title="重做"><span class="teamworkIcon">&middot;</span></button>
      <span class="ganttButtonSeparator requireCanWrite requireCanAdd"></span>
      <button onclick="$('#workSpace').trigger('addAboveCurrentTask.gantt');return false;" class="button textual icon requireCanWrite requireCanAdd" title="在上方插入"><span class="teamworkIcon">l</span></button>
      <button onclick="$('#workSpace').trigger('addBelowCurrentTask.gantt');return false;" class="button textual icon requireCanWrite requireCanAdd" title="在下方插入"><span class="teamworkIcon">X</span></button>
      <span class="ganttButtonSeparator requireCanWrite requireCanInOutdent"></span>
      <button onclick="$('#workSpace').trigger('outdentCurrentTask.gantt');return false;" class="button textual icon requireCanWrite requireCanInOutdent" title="升级"><span class="teamworkIcon">.</span></button>
      <button onclick="$('#workSpace').trigger('indentCurrentTask.gantt');return false;" class="button textual icon requireCanWrite requireCanInOutdent" title="降级"><span class="teamworkIcon">:</span></button>
      <span class="ganttButtonSeparator requireCanWrite requireCanMoveUpDown"></span>
      <button onclick="$('#workSpace').trigger('moveUpCurrentTask.gantt');return false;" class="button textual icon requireCanWrite requireCanMoveUpDown" title="上移"><span class="teamworkIcon">k</span></button>
      <button onclick="$('#workSpace').trigger('moveDownCurrentTask.gantt');return false;" class="button textual icon requireCanWrite requireCanMoveUpDown" title="下移"><span class="teamworkIcon">j</span></button>
      <span class="ganttButtonSeparator requireCanWrite requireCanDelete"></span>
      <button onclick="$('#workSpace').trigger('deleteFocused.gantt');return false;" class="button textual icon delete requireCanWrite" title="删除"><span class="teamworkIcon">&cent;</span></button>
      <span class="ganttButtonSeparator"></span>
      <button onclick="$('#workSpace').trigger('expandAll.gantt');return false;" class="button textual icon " title="全部展开"><span class="teamworkIcon">6</span></button>
      <button onclick="$('#workSpace').trigger('collapseAll.gantt'); return false;" class="button textual icon " title="全部折叠"><span class="teamworkIcon">5</span></button>

    <span class="ganttButtonSeparator"></span>
      <button onclick="$('#workSpace').trigger('zoomMinus.gantt'); return false;" class="button textual icon " title="缩小"><span class="teamworkIcon">)</span></button>
      <button onclick="$('#workSpace').trigger('zoomPlus.gantt');return false;" class="button textual icon " title="放大"><span class="teamworkIcon">(</span></button>

    <span class="ganttButtonSeparator"></span>
    
      <button onclick="ge.splitter.resize(.1);return false;" class="button textual icon" title="右侧最大化"><span class="teamworkIcon">F</span></button>
      <button onclick="ge.splitter.resize(50);return false;" class="button textual icon" title="平均显示"><span class="teamworkIcon">O</span></button>
      <button onclick="ge.splitter.resize(100);return false;" class="button textual icon" title="左侧最大化"><span class="teamworkIcon">R</span></button>
      <span class="ganttButtonSeparator"></span>



      &nbsp; &nbsp; &nbsp; &nbsp;
    </div>

    <div>

    </div>
  </div>
  -->
</div>

<div class="__template__" type="TASKSEDITHEAD"><!--
  <table class="gdfTable" cellspacing="0" cellpadding="0">
    <thead>
    <tr style="height:40px">
      <th class="gdfColHeader" style="width:35px; border-right: none"></th>
      <th class="gdfColHeader" style="width:25px;"></th>
      <th class="gdfColHeader gdfResizable" style="width:75px;">任务编码</th>
      <th class="gdfColHeader gdfResizable" style="width:300px;">任务名称</th>
      <th class="gdfColHeader"  align="center" style="width:17px;" title="开始日期是里程碑。"><span class="teamworkIcon" style="font-size: 8px;">^</span></th>
      <th class="gdfColHeader gdfResizable" style="width:80px;">开始日期</th>
      <th class="gdfColHeader"  align="center" style="width:17px;" title="结束日期是里程碑。"><span class="teamworkIcon" style="font-size: 8px;">^</span></th>
      <th class="gdfColHeader gdfResizable" style="width:80px;">结束日期</th>
      <th class="gdfColHeader gdfResizable" style="width:60px;">工期(天)</th>
      <th class="gdfColHeader gdfResizable" style="width:60px;">进度(%)</th>
      <th class="gdfColHeader gdfResizable requireCanSeeDep" style="width:50px;">依赖项</th>
      <th class="gdfColHeader gdfResizable" style="width:80px;">负责人</th> <!-- Added Responsible column header -->
      <th class="gdfColHeader gdfResizable" style="width:1000px; text-align: left; padding-left: 10px;">负责人</th>
    </tr>
    </thead>
  </table>
  --></div>

<div class="__template__" type="TASKROW"><!--
  <tr id="tid_(#=obj.id#)" taskId="(#=obj.id#)" class="taskEditRow (#=obj.isParent()?'isParent':''#) (#=obj.collapsed?'collapsed':''#)" level="(#=level#)">
    <th class="gdfCell edit" align="right" style="cursor:pointer;"><span class="taskRowIndex">(#=obj.getRow()+1#)</span> <span class="teamworkIcon" style="font-size:12px;" >e</span></th>
    <td class="gdfCell noClip" align="center"><div class="taskStatus cvcColorSquare" status="(#=obj.status#)"></div></td>
    <td class="gdfCell"><input type="text" name="code" value="(#=obj.code?obj.code:''#)" placeholder=""></td>
    <td class="gdfCell indentCell" style="padding-left:(#=obj.level*10+18#)px;">
      <div class="exp-controller" align="center"></div>
      <input type="text" name="name" value="(#=obj.name#)" placeholder="任务名称">
    </td>
    <td class="gdfCell" align="center"><input type="checkbox" name="startIsMilestone"></td>
    <td class="gdfCell"><input type="text" name="start"  value="" class="date"></td>
    <td class="gdfCell" align="center"><input type="checkbox" name="endIsMilestone"></td>
    <td class="gdfCell"><input type="text" name="end" value="" class="date"></td>
    <td class="gdfCell"><input type="text" name="duration" autocomplete="off" value="(#=obj.duration#)"></td>
    <td class="gdfCell"><input type="text" name="progress" class="validated" entrytype="PERCENTILE" autocomplete="off" value="(#=obj.progress?obj.progress:''#)" (#=obj.progressByWorklog?"readOnly":""#)></td>
    <td class="gdfCell requireCanSeeDep"><input type="text" name="depends" autocomplete="off" value="(#=obj.depends#)" (#=obj.hasExternalDep?"readonly":""#)></td>
    <td class="gdfCell"><input type="text" name="responsible" value="(#=obj.responsible?obj.responsible:''#)" placeholder=""></td> <!-- Added Responsible cell -->
    <td class="gdfCell taskAssigs">(#=obj.getAssigsString()#)</td>
  </tr>
  --></div>

<div class="__template__" type="TASKEMPTYROW"><!--
  <tr class="taskEditRow emptyRow" >
    <th class="gdfCell" align="right"></th>
    <td class="gdfCell noClip" align="center"></td>
    <td class="gdfCell"></td>
    <td class="gdfCell"></td>
    <td class="gdfCell"></td>
    <td class="gdfCell"></td>
    <td class="gdfCell"></td>
    <td class="gdfCell"></td>
    <td class="gdfCell"></td>
    <td class="gdfCell"></td>
    <td class="gdfCell requireCanSeeDep"></td>
    <td class="gdfCell"></td>
  </tr>
  --></div>

<div class="__template__" type="TASKBAR"><!--
  <div class="taskBox taskBoxDiv" taskId="(#=obj.id#)" >
    <div class="layout (#=obj.hasExternalDep?'extDep':''#)">
      <div class="taskStatus" status="(#=obj.status#)"></div>
      <div class="taskProgress" style="width:(#=obj.progress>100?100:obj.progress#)%; background-color:(#=obj.progress>100?'red':'rgb(153,255,51);'#);"></div>
      <div class="milestone (#=obj.startIsMilestone?'active':''#)" ></div>

      <div class="taskLabel"></div>
      <div class="milestone end (#=obj.endIsMilestone?'active':''#)" ></div>
    </div>
  </div>
  --></div>


<div class="__template__" type="CHANGE_STATUS"><!--
    <div class="taskStatusBox">
    <div class="taskStatus cvcColorSquare" status="STATUS_ACTIVE" title="进行中"></div>
    <div class="taskStatus cvcColorSquare" status="STATUS_WAITING" title="等待中"></div>
    <div class="taskStatus cvcColorSquare" status="STATUS_SUSPENDED" title="已暂停"></div>
    <div class="taskStatus cvcColorSquare" status="STATUS_DONE" title="已完成"></div>
    <div class="taskStatus cvcColorSquare" status="STATUS_FAILED" title="已失败"></div>     
    <div class="taskStatus cvcColorSquare" status="STATUS_UNDEFINED" title="未定义"></div>
    </div>
  --></div>




<div class="__template__" type="TASK_EDITOR"><!--
  <div class="ganttTaskEditor">
    <h2 class="taskData">任务编辑器</h2>
    <table  cellspacing="1" cellpadding="5" width="100%" class="taskData table" border="0">
          <tr>
        <td width="200" style="height: 80px"  valign="top">
          <label for="code">任务编码</label><br>
          <input type="text" name="code" id="code" value="" size=15 class="formElements" autocomplete='off' maxlength=255 style='width:100%' oldvalue="1">
        </td>
        <td colspan="3" valign="top"><label for="name" class="required">任务名称</label><br><input type="text" name="name" id="name"class="formElements" autocomplete='off' maxlength=255 style='width:100%' value="" required="true" oldvalue="1"></td>
          </tr>


      <tr class="dateRow">
        <td nowrap="">
          <div style="position:relative">
            <label for="start">开始日期</label>&nbsp;&nbsp;&nbsp;&nbsp;
            <input type="checkbox" id="startIsMilestone" name="startIsMilestone" value="yes"> &nbsp;<label for="startIsMilestone">里程碑</label>&nbsp;
            <br><input type="text" name="start" id="start" size="8" class="formElements dateField validated date" autocomplete="off" maxlength="255" value="" oldvalue="1" entrytype="DATE">
            <span title="日历" id="starts_inputDate" class="teamworkIcon openCalendar" onclick="$(this).dateField({inputField:$(this).prevAll(':input:first'),isSearchField:false});">m</span>          </div>
        </td>
        <td nowrap="">
          <label for="end">结束日期</label>&nbsp;&nbsp;&nbsp;&nbsp;
          <input type="checkbox" id="endIsMilestone" name="endIsMilestone" value="yes"> &nbsp;<label for="endIsMilestone">里程碑</label>&nbsp;
          <br><input type="text" name="end" id="end" size="8" class="formElements dateField validated date" autocomplete="off" maxlength="255" value="" oldvalue="1" entrytype="DATE">
          <span title="日历" id="ends_inputDate" class="teamworkIcon openCalendar" onclick="$(this).dateField({inputField:$(this).prevAll(':input:first'),isSearchField:false});">m</span>
        </td>
        <td nowrap="" >
          <label for="duration" class=" ">工期(天)</label><br>
          <input type="text" name="duration" id="duration" size="4" class="formElements validated durationdays" title="工期以工作日计算。" autocomplete="off" maxlength="255" value="" oldvalue="1" entrytype="DURATIONDAYS">&nbsp;
        </td>
      </tr>

      <tr>
        <td  colspan="2">
          <label for="status" class=" ">状态</label><br>
          <select id="status" name="status" class="taskStatus" status="(#=obj.status#)"  onchange="$(this).attr('STATUS',$(this).val());">
            <option value="STATUS_ACTIVE" class="taskStatus" status="STATUS_ACTIVE" >进行中</option>
            <option value="STATUS_WAITING" class="taskStatus" status="STATUS_WAITING" >等待中</option>
            <option value="STATUS_SUSPENDED" class="taskStatus" status="STATUS_SUSPENDED" >已暂停</option>
            <option value="STATUS_DONE" class="taskStatus" status="STATUS_DONE" >已完成</option>
            <option value="STATUS_FAILED" class="taskStatus" status="STATUS_FAILED" >已失败</option>
            <option value="STATUS_UNDEFINED" class="taskStatus" status="STATUS_UNDEFINED" >未定义</option>
          </select>
        </td>

        <td valign="top" nowrap>
          <label>进度(%)</label><br>
          <input type="text" name="progress" id="progress" size="7" class="formElements validated percentile" autocomplete="off" maxlength="255" value="" oldvalue="1" entrytype="PERCENTILE">
        </td>
      </tr>

          </tr>
          <tr>
            <td colspan="4">
              <label for="description">描述</label><br>
              <textarea rows="3" cols="30" id="description" name="description" class="formElements" style="width:100%"></textarea>
            </td>
          </tr>
        </table>

   

  <div style="text-align: right; padding-top: 20px">
    <span id="saveButton" class="button first" onClick="$(this).trigger('saveFullEditor.gantt');">保存</span>
  </div>

  </div>
  --></div>



<div class="__template__" type="ASSIGNMENT_ROW"><!--
  <tr taskId="(#=obj.task.id#)" assId="(#=obj.assig.id#)" class="assigEditRow" >
    <td ><select name="resourceId"  class="formElements" (#=obj.assig.id.indexOf("tmp_")==0?"":"disabled"#) ></select></td>
    <td ><select type="select" name="roleId"  class="formElements"></select></td>
    <td ><input type="text" name="effort" value="(#=getMillisInHoursMinutes(obj.assig.effort)#)" size="5" class="formElements"></td>
    <td align="center"><span class="teamworkIcon delAssig del" style="cursor: pointer">d</span></td>
  </tr>
  --></div>



<div class="__template__" type="RESOURCE_EDITOR"><!--
  <div class="resourceEditor" style="padding: 5px;">

    <h2>项目团队</h2>
    <table  cellspacing="1" cellpadding="0" width="100%" id="resourcesTable">
      <tr>
        <th style="width:100px;">名称</th>
        <th style="width:30px;" id="addResource"><span class="teamworkIcon" style="cursor: pointer">+</span></th>
      </tr>
    </table>

    <div style="text-align: right; padding-top: 20px"><button id="resSaveButton" class="button big">保存</button></div>
  </div>
  --></div>



<div class="__template__" type="RESOURCE_ROW"><!--
  <tr resId="(#=obj.id#)" class="resRow" >
    <td ><input type="text" name="name" value="(#=obj.name#)" style="width:100%;" class="formElements"></td>
    <td align="center"><span class="teamworkIcon delRes del" style="cursor: pointer">d</span></td>
  </tr>
  --></div>


</div>
<script type="text/javascript">
  $.JST.loadDecorator("RESOURCE_ROW", function(resTr, res){
    resTr.find(".delRes").click(function(){$(this).closest("tr").remove()});
  });

  $.JST.loadDecorator("ASSIGNMENT_ROW", function(assigTr, taskAssig){
    var resEl = assigTr.find("[name=resourceId]");
    var opt = $("<option>");
    resEl.append(opt);
    for(var i=0; i< taskAssig.task.master.resources.length;i++){
      var res = taskAssig.task.master.resources[i];
      opt = $("<option>");
      opt.val(res.id).html(res.name);
      if(taskAssig.assig.resourceId == res.id)
        opt.attr("selected", "true");
      resEl.append(opt);
    }
    var roleEl = assigTr.find("[name=roleId]");
    for(var i=0; i< taskAssig.task.master.roles.length;i++){
      var role = taskAssig.task.master.roles[i];
      var optr = $("<option>");
      optr.val(role.id).html(role.name);
      if(taskAssig.assig.roleId == role.id)
        optr.attr("selected", "true");
      roleEl.append(optr);
    }

    if(taskAssig.task.master.permissions.canWrite && taskAssig.task.canWrite){
      assigTr.find(".delAssig").click(function(){
        var tr = $(this).closest("[assId]").fadeOut(200, function(){$(this).remove()});
      });
    }

  });


  function loadI18n(){
    GanttMaster.messages = {
      "CANNOT_WRITE":"无权限修改以下任务：",
      "CHANGE_OUT_OF_SCOPE":"无法更新项目，因为您没有更新父项目的权限。",
      "START_IS_MILESTONE":"开始日期是里程碑。",
      "END_IS_MILESTONE":"结束日期是里程碑。",
      "TASK_HAS_CONSTRAINTS":"任务存在约束。",
      "GANTT_ERROR_DEPENDS_ON_OPEN_TASK":"错误：存在对未关闭任务的依赖。",
      "GANTT_ERROR_DESCENDANT_OF_CLOSED_TASK":"错误：存在已关闭任务的子任务。",
      "TASK_HAS_EXTERNAL_DEPS":"此任务存在外部依赖。",
      "GANNT_ERROR_LOADING_DATA_TASK_REMOVED":"加载数据时出错。任务已被删除。", // Using the more descriptive message
      "CIRCULAR_REFERENCE":"循环引用。",
      "CANNOT_DEPENDS_ON_ANCESTORS":"不能依赖于父任务。",
      "INVALID_DATE_FORMAT":"输入的数据格式无效。",
      "GANTT_ERROR_LOADING_DATA_TASK_REMOVED":"加载数据时出错。任务已被删除。", // Duplicate key, ensure consistency or remove if truly duplicate
      "CANNOT_CLOSE_TASK_IF_OPEN_ISSUE":"无法关闭存在未解决问题的任务。",
      "TASK_MOVE_INCONSISTENT_LEVEL":"无法交换不同层级的任务。",
      "CANNOT_MOVE_TASK":"无法移动任务",
      "PLEASE_SAVE_PROJECT":"请保存项目",
      "GANTT_SEMESTER":"半年", // Using 上半年/下半年 might be too long for header, using 半年
      "GANTT_SEMESTER_SHORT":"半年",
      "GANTT_QUARTER":"季度",
      "GANTT_QUARTER_SHORT":"季",
      "GANTT_WEEK":"周",
      "GANTT_WEEK_SHORT":"周" // Keep '周' for short week as well
    };
  }



  function createNewResource(el) {
    var row = el.closest("tr[taskid]");
    var name = row.find("[name=resourceId_txt]").val();
    var url = contextPath + "/applications/teamwork/resource/resourceNew.jsp?CM=ADD&name=" + encodeURI(name);

    openBlackPopup(url, 700, 320, function (response) {
      //fillare lo smart combo
      if (response && response.resId && response.resName) {
        //fillare lo smart combo e chiudere l'editor
        row.find("[name=resourceId]").val(response.resId);
        row.find("[name=resourceId_txt]").val(response.resName).focus().blur();
      }

    });
  }

$(document).on("change", "#load-file", function() {
  var uploadedFile = $("#load-file").prop("files")[0];
  upload(uploadedFile);
});

</script>




</body>
</html>